Criterion B Details

# Default ecosystem code for template development.
# This line is replaced by build_ecosystem_pages.py for each ecosystem.
ecosystem_code = 'Fondos Blandos con Vegetacion No Vascular'

Import Python modules.

import os
import yaml
from pathlib import Path
from lonboard import Map
from rle.core import Ecosystems, criterion_b_status, rle_category
from rle.core.eoo import make_eoo
from rle.core.aoo import make_aoo_grid

Load the country config file.

project_root = os.environ.get('PIXI_PROJECT_ROOT', str(Path('..').resolve()))
config_path = Path(project_root) / 'config' / 'country_config.yaml'
with open(config_path) as f:
    config = yaml.safe_load(f)

# Ecosystem index (the COG pixel value for this ecosystem), looked up by code
# from the canonical index table. Absent if the index has not been built yet.
import csv
ecosystem_index = None
index_csv = Path(project_root) / 'config' / 'ecosystems' / 'index.csv'
if index_csv.exists():
    with open(index_csv) as f:
        for row in csv.DictReader(f):
            if row['code'] == ecosystem_code:
                ecosystem_index = int(row['index'])
                break

Load & Filter Ecosystem Data

Load data for all the ecosystems.

import sys
# Shared config helpers live in scripts/; make them importable from here.
sys.path.insert(0, str(Path(project_root) / 'scripts'))
from _config import ensure_vector_source

source = config['ecosystem_source']
# ecosystem_code_column is optional: fall back to the name column.
ecosystem_column = source.get('ecosystem_code_column') or source.get('ecosystem_name_column')
# Prefer the ecosystem-sorted `optimized_data` copy when configured, so that
# filtering to one ecosystem uses parquet predicate pushdown instead of loading
# the whole national map into memory. Falls back to `data`.
ecosystems = Ecosystems.from_file(
    ensure_vector_source(source.get('optimized_data') or source['data']),
    ecosystem_column=ecosystem_column,
    ecosystem_name_column=source.get('ecosystem_name_column'),
    functional_group_column=source.get('functional_group_column')
)

Filter by the Fondos Blandos con Vegetacion No Vascular and check the number of features.

ecosystem = ecosystems.filter(ecosystem_code)
has_data = ecosystem.size() > 0
print(f'{ecosystem.size() = }')
if not has_data:
    from IPython.display import Markdown, display
    display(Markdown(
        f'**No spatial data found for {ecosystem_code}.** '
        f'Criterion B calculations are skipped.'
    ))
ecosystem.size() = 70

Extent of Occurrence (EOO) (subcriterion B1)

Extent of occurrence (EOO). The EOO of an ecosystem is the area (km2) of a minimum convex polygon – the smallest polygon in which no internal angle exceeds 180° that encompasses all known current spatial occurrences of the ecosystem type.

The minimum convex polygon (also known as a convex hull) must not exclude any areas, discontinuities or disjunctions, regardless of whether the ecosystem can occur in those areas or not. Regions such as oceans (for terrestrial ecosystems), land (for coastal or marine ecosystems), or areas outside the study area (such as in a different country) must remain included within the minimum convex polygon to ensure that this standardised method is comparable across ecosystem types. In addition, these features contribute to spreading risks across the distribution of the ecosystem by making different parts of its distribution more spatially independent.

Calculate EOO

Start by calculating the convex hull of the ecosystem’s distribution.

import geopandas as gpd

if has_data:
    ecosystem_geometry = ecosystem.geometry.union_all()
    gdf_ecosystem_polygons = gpd.GeoDataFrame(geometry=[ecosystem_geometry], crs=ecosystem.geometry.crs)
    hull = ecosystem_geometry.convex_hull
    gdf_hull = gpd.GeoDataFrame(geometry=[hull], crs=ecosystem.geometry.crs)

Display the ecosystem’s distribution and the convex hull.

from lonboard import Map, PolygonLayer
from rle.core.viz import smart_map

if has_data:
    eoo_hull = make_eoo(ecosystem).compute()
    display(smart_map([eoo_hull, ecosystem]))
/home/runner/work/rle-tyler-colombia/rle-tyler-colombia/.pixi/envs/default/lib/python3.11/site-packages/lonboard/_geoarrow/ops/reproject.py:116: UserWarning: Input being reprojected to EPSG:4326 CRS.
Lonboard is only able to render data in EPSG:4326 projection.
  warnings.warn(
/home/runner/work/rle-tyler-colombia/rle-tyler-colombia/.pixi/envs/default/lib/python3.11/site-packages/lonboard/_geoarrow/ops/reproject.py:116: UserWarning: Input being reprojected to EPSG:4326 CRS.
Lonboard is only able to render data in EPSG:4326 projection.
  warnings.warn(
if has_data:
    hull_ea = gdf_hull.to_crs("ESRI:54034")
    eoo = hull_ea.geometry.iloc[0].area / 1e6
    print(f'EOO is {eoo:.1f} km2')
EOO is 234302.8 km2

Then calculate the area of the convex hull polygon.

Direct calculation of EOO

EOO can also be calculated directly using …

if has_data:
    ecosystem.eoo

Verify that the area returned by calling make_eoo(ecosystem).compute().area_km2 is the same as the area of the convex hull polygon.

if has_data:
    assert ecosystem.eoo == eoo

Area of Occupancy (AOO) (subcriterion B2)

The protocol for this adjustment includes the following steps:

  1. Intersect AOO grid with the ecosystem’s distribution map.
  2. Calculate extent of the ecosystem type in each grid cell (area) and sum these areas to obtain the total ecosystem area (total area).
  3. Arrange grid cells in ascending order based on their area (smaller first). Calculate accumulated sum of area per cell (cumulative area).
  4. Calculate cumulative proportion by dividing cumulative area by total area (cumulative proportion takes values between 0 and 1)
  5. Calculate AOO by counting the number of cells with a cumulative proportion greater than 0.01 (i.e. exclude cells that in combination account for up to 1% of the total mapped extent of the ecosystem type).

AOO Calculation Details

Intersect AOO grid and ecosystem map

  1. Intersect AOO grid with the ecosystem’s distribution map
from pathlib import Path
from rle.core.aoo import make_aoo_grid_cached

if has_data:
    # Prefer a prebuilt grid cache (e.g. a gs:// URI) so the national AOO grid
    # is not recomputed during CI renders — computing it from the full national
    # ecosystem map peaks at many GB of RAM. Fall back to a local cache when no
    # prebuilt cache is configured. Build one with `pixi run build-caches`.
    cache_path = (
        source.get('aoo_grid_cache_url')
        or (Path(project_root) / '.cache' / 'aoo_grid.parquet')
    )
    aoo_grid = make_aoo_grid_cached(ecosystems, cache_path=cache_path)
    aoo_grid_filtered = aoo_grid.filter_by_ecosystem(ecosystem_code)

Visualize variations in the AOO grid.

from matplotlib.colors import LinearSegmentedColormap
from lonboard.colormap import apply_continuous_cmap
from rle.core.aoo import slugify_ecosystem_name

ecosystem_column = slugify_ecosystem_name(ecosystem_code)
if has_data:
    cmap = LinearSegmentedColormap.from_list("white_red", ["white", "red"])
    values = aoo_grid_filtered.grid_cells[ecosystem_column].values
    normalized = (values - values.min()) / (values.max() - values.min())
    colors = apply_continuous_cmap(normalized, cmap)
    display(smart_map([(aoo_grid_filtered, {"get_fill_color": colors}), ecosystem]))
/home/runner/work/rle-tyler-colombia/rle-tyler-colombia/.pixi/envs/default/lib/python3.11/site-packages/lonboard/_geoarrow/ops/reproject.py:116: UserWarning: Input being reprojected to EPSG:4326 CRS.
Lonboard is only able to render data in EPSG:4326 projection.
  warnings.warn(

Calculate grid cell area and total area

  1. Calculate extent of the ecosystem type in each grid cell (area) and sum these areas to obtain the total ecosystem area (total area).
if has_data:
    keep = ['geometry', 'grid_col', 'grid_row', ecosystem_column]
    gdf = aoo_grid_filtered.grid_cells[keep]
    display(gdf)
geometry grid_col grid_row Fondos_Blandos_con_Vegetacion_No_Vascular
0 POLYGON ((-77.43478 3.80106, -77.43478 3.8917,... -863 42 0.190937
1 POLYGON ((-77.43478 3.8917, -77.43478 3.98235,... -863 43 0.237355
2 POLYGON ((-77.43478 3.98235, -77.43478 4.073, ... -863 44 0.074397
3 POLYGON ((-77.34495 3.80106, -77.34495 3.8917,... -862 42 0.879010
4 POLYGON ((-77.34495 3.8917, -77.34495 3.98235,... -862 43 0.722770
5 POLYGON ((-77.34495 3.98235, -77.34495 4.073, ... -862 44 0.056245
6 POLYGON ((-77.34495 8.62317, -77.34495 8.71462... -862 95 0.001035
7 POLYGON ((-77.25511 3.80106, -77.25511 3.8917,... -861 42 0.527619
8 POLYGON ((-77.25511 3.8917, -77.25511 3.98235,... -861 43 0.373268
9 POLYGON ((-77.25511 3.98235, -77.25511 4.073, ... -861 44 0.491709
10 POLYGON ((-77.25511 8.53173, -77.25511 8.62317... -861 94 0.013660
11 POLYGON ((-77.25511 8.62317, -77.25511 8.71462... -861 95 0.004169
12 POLYGON ((-77.16528 3.8917, -77.16528 3.98235,... -860 43 0.004273
13 POLYGON ((-77.16528 3.98235, -77.16528 4.073, ... -860 44 0.369772
14 POLYGON ((-77.07545 8.25756, -77.07545 8.34893... -859 91 0.026301
15 POLYGON ((-77.07545 8.34893, -77.07545 8.44032... -859 92 0.034110
16 POLYGON ((-76.98562 8.25756, -76.98562 8.34893... -858 91 0.070095
17 POLYGON ((-75.63815 9.6304, -75.63815 9.72211,... -843 106 0.002910
18 POLYGON ((-75.63815 9.72211, -75.63815 9.81384... -843 107 0.018503
19 POLYGON ((-75.54832 9.72211, -75.54832 9.81384... -842 107 0.011249
20 POLYGON ((-75.54832 9.81384, -75.54832 9.9056,... -842 108 0.002594
21 POLYGON ((-75.54832 9.9056, -75.54832 9.99738,... -842 109 0.037019
22 POLYGON ((-74.47034 11.0087, -74.47034 11.1008... -830 121 0.074121
23 POLYGON ((-74.38051 11.0087, -74.38051 11.1008... -829 121 0.331935
24 POLYGON ((-74.38051 11.1008, -74.38051 11.1929... -829 122 0.018344
25 POLYGON ((-74.29067 11.0087, -74.29067 11.1008... -828 121 0.088944
26 POLYGON ((-74.29067 11.1008, -74.29067 11.1929... -828 122 0.115512
27 POLYGON ((-74.02118 11.28509, -74.02118 11.377... -825 124 0.003229
28 POLYGON ((-72.04489 12.2083, -72.04489 12.3007... -803 134 0.003724
29 POLYGON ((-71.95505 12.2083, -71.95505 12.3007... -802 134 0.063366
30 POLYGON ((-71.68556 12.39331, -71.68556 12.485... -799 136 0.000196
31 POLYGON ((-71.59573 12.39331, -71.59573 12.485... -798 136 0.000873
32 POLYGON ((-71.32623 12.30079, -71.32623 12.393... -795 135 0.001667
33 POLYGON ((-71.2364 11.83865, -71.2364 11.93102... -794 130 0.004086
34 POLYGON ((-71.2364 11.93102, -71.2364 12.02342... -794 131 0.001276
35 POLYGON ((-71.2364 12.30079, -71.2364 12.39331... -794 135 0.018784
36 POLYGON ((-71.14657 12.11584, -71.14657 12.208... -793 133 0.000943
37 POLYGON ((-71.14657 12.2083, -71.14657 12.3007... -793 134 0.003270
38 POLYGON ((-71.14657 12.30079, -71.14657 12.393... -793 135 0.001029
39 POLYGON ((-71.05674 12.11584, -71.05674 12.208... -792 133 0.000863

The column Fondos_Blandos_con_Vegetacion_No_Vascular contains the (fractional) area of the ecosystem in each grid cell.

Sum up the areas of each grid cell to get the total area.

if has_data:
    total_area = gdf[ecosystem_column].sum()
    display(total_area)
np.float64(4.881160452166059)

Calculate cumulative area

  1. Arrange grid cells in ascending order based on their area (smaller first). Calculate accumulated sum of area per cell (cumulative area).
if has_data:
    gdf = gdf.sort_values(by=ecosystem_column)
    gdf["cumulative_area"] = gdf[ecosystem_column].cumsum()
    display(gdf)
geometry grid_col grid_row Fondos_Blandos_con_Vegetacion_No_Vascular cumulative_area
30 POLYGON ((-71.68556 12.39331, -71.68556 12.485... -799 136 0.000196 0.000196
39 POLYGON ((-71.05674 12.11584, -71.05674 12.208... -792 133 0.000863 0.001059
31 POLYGON ((-71.59573 12.39331, -71.59573 12.485... -798 136 0.000873 0.001933
36 POLYGON ((-71.14657 12.11584, -71.14657 12.208... -793 133 0.000943 0.002876
38 POLYGON ((-71.14657 12.30079, -71.14657 12.393... -793 135 0.001029 0.003904
6 POLYGON ((-77.34495 8.62317, -77.34495 8.71462... -862 95 0.001035 0.004939
34 POLYGON ((-71.2364 11.93102, -71.2364 12.02342... -794 131 0.001276 0.006216
32 POLYGON ((-71.32623 12.30079, -71.32623 12.393... -795 135 0.001667 0.007882
20 POLYGON ((-75.54832 9.81384, -75.54832 9.9056,... -842 108 0.002594 0.010477
17 POLYGON ((-75.63815 9.6304, -75.63815 9.72211,... -843 106 0.002910 0.013386
27 POLYGON ((-74.02118 11.28509, -74.02118 11.377... -825 124 0.003229 0.016615
37 POLYGON ((-71.14657 12.2083, -71.14657 12.3007... -793 134 0.003270 0.019884
28 POLYGON ((-72.04489 12.2083, -72.04489 12.3007... -803 134 0.003724 0.023608
33 POLYGON ((-71.2364 11.83865, -71.2364 11.93102... -794 130 0.004086 0.027694
11 POLYGON ((-77.25511 8.62317, -77.25511 8.71462... -861 95 0.004169 0.031863
12 POLYGON ((-77.16528 3.8917, -77.16528 3.98235,... -860 43 0.004273 0.036135
19 POLYGON ((-75.54832 9.72211, -75.54832 9.81384... -842 107 0.011249 0.047384
10 POLYGON ((-77.25511 8.53173, -77.25511 8.62317... -861 94 0.013660 0.061045
24 POLYGON ((-74.38051 11.1008, -74.38051 11.1929... -829 122 0.018344 0.079389
18 POLYGON ((-75.63815 9.72211, -75.63815 9.81384... -843 107 0.018503 0.097892
35 POLYGON ((-71.2364 12.30079, -71.2364 12.39331... -794 135 0.018784 0.116675
14 POLYGON ((-77.07545 8.25756, -77.07545 8.34893... -859 91 0.026301 0.142976
15 POLYGON ((-77.07545 8.34893, -77.07545 8.44032... -859 92 0.034110 0.177086
21 POLYGON ((-75.54832 9.9056, -75.54832 9.99738,... -842 109 0.037019 0.214105
5 POLYGON ((-77.34495 3.98235, -77.34495 4.073, ... -862 44 0.056245 0.270350
29 POLYGON ((-71.95505 12.2083, -71.95505 12.3007... -802 134 0.063366 0.333717
16 POLYGON ((-76.98562 8.25756, -76.98562 8.34893... -858 91 0.070095 0.403812
22 POLYGON ((-74.47034 11.0087, -74.47034 11.1008... -830 121 0.074121 0.477932
2 POLYGON ((-77.43478 3.98235, -77.43478 4.073, ... -863 44 0.074397 0.552329
25 POLYGON ((-74.29067 11.0087, -74.29067 11.1008... -828 121 0.088944 0.641273
26 POLYGON ((-74.29067 11.1008, -74.29067 11.1929... -828 122 0.115512 0.756785
0 POLYGON ((-77.43478 3.80106, -77.43478 3.8917,... -863 42 0.190937 0.947722
1 POLYGON ((-77.43478 3.8917, -77.43478 3.98235,... -863 43 0.237355 1.185077
23 POLYGON ((-74.38051 11.0087, -74.38051 11.1008... -829 121 0.331935 1.517012
13 POLYGON ((-77.16528 3.98235, -77.16528 4.073, ... -860 44 0.369772 1.886784
8 POLYGON ((-77.25511 3.8917, -77.25511 3.98235,... -861 43 0.373268 2.260052
9 POLYGON ((-77.25511 3.98235, -77.25511 4.073, ... -861 44 0.491709 2.751761
7 POLYGON ((-77.25511 3.80106, -77.25511 3.8917,... -861 42 0.527619 3.279380
4 POLYGON ((-77.34495 3.8917, -77.34495 3.98235,... -862 43 0.722770 4.002150
3 POLYGON ((-77.34495 3.80106, -77.34495 3.8917,... -862 42 0.879010 4.881160

Calculate cumulative proportion

  1. Calculate cumulative proportion by dividing cumulative area by total area (cumulative proportion takes values between 0 and 1)
if has_data:
    gdf["cumulative_proportion"] = gdf["cumulative_area"] / total_area
    display(gdf)
geometry grid_col grid_row Fondos_Blandos_con_Vegetacion_No_Vascular cumulative_area cumulative_proportion
30 POLYGON ((-71.68556 12.39331, -71.68556 12.485... -799 136 0.000196 0.000196 0.000040
39 POLYGON ((-71.05674 12.11584, -71.05674 12.208... -792 133 0.000863 0.001059 0.000217
31 POLYGON ((-71.59573 12.39331, -71.59573 12.485... -798 136 0.000873 0.001933 0.000396
36 POLYGON ((-71.14657 12.11584, -71.14657 12.208... -793 133 0.000943 0.002876 0.000589
38 POLYGON ((-71.14657 12.30079, -71.14657 12.393... -793 135 0.001029 0.003904 0.000800
6 POLYGON ((-77.34495 8.62317, -77.34495 8.71462... -862 95 0.001035 0.004939 0.001012
34 POLYGON ((-71.2364 11.93102, -71.2364 12.02342... -794 131 0.001276 0.006216 0.001273
32 POLYGON ((-71.32623 12.30079, -71.32623 12.393... -795 135 0.001667 0.007882 0.001615
20 POLYGON ((-75.54832 9.81384, -75.54832 9.9056,... -842 108 0.002594 0.010477 0.002146
17 POLYGON ((-75.63815 9.6304, -75.63815 9.72211,... -843 106 0.002910 0.013386 0.002742
27 POLYGON ((-74.02118 11.28509, -74.02118 11.377... -825 124 0.003229 0.016615 0.003404
37 POLYGON ((-71.14657 12.2083, -71.14657 12.3007... -793 134 0.003270 0.019884 0.004074
28 POLYGON ((-72.04489 12.2083, -72.04489 12.3007... -803 134 0.003724 0.023608 0.004837
33 POLYGON ((-71.2364 11.83865, -71.2364 11.93102... -794 130 0.004086 0.027694 0.005674
11 POLYGON ((-77.25511 8.62317, -77.25511 8.71462... -861 95 0.004169 0.031863 0.006528
12 POLYGON ((-77.16528 3.8917, -77.16528 3.98235,... -860 43 0.004273 0.036135 0.007403
19 POLYGON ((-75.54832 9.72211, -75.54832 9.81384... -842 107 0.011249 0.047384 0.009708
10 POLYGON ((-77.25511 8.53173, -77.25511 8.62317... -861 94 0.013660 0.061045 0.012506
24 POLYGON ((-74.38051 11.1008, -74.38051 11.1929... -829 122 0.018344 0.079389 0.016264
18 POLYGON ((-75.63815 9.72211, -75.63815 9.81384... -843 107 0.018503 0.097892 0.020055
35 POLYGON ((-71.2364 12.30079, -71.2364 12.39331... -794 135 0.018784 0.116675 0.023903
14 POLYGON ((-77.07545 8.25756, -77.07545 8.34893... -859 91 0.026301 0.142976 0.029291
15 POLYGON ((-77.07545 8.34893, -77.07545 8.44032... -859 92 0.034110 0.177086 0.036279
21 POLYGON ((-75.54832 9.9056, -75.54832 9.99738,... -842 109 0.037019 0.214105 0.043864
5 POLYGON ((-77.34495 3.98235, -77.34495 4.073, ... -862 44 0.056245 0.270350 0.055387
29 POLYGON ((-71.95505 12.2083, -71.95505 12.3007... -802 134 0.063366 0.333717 0.068368
16 POLYGON ((-76.98562 8.25756, -76.98562 8.34893... -858 91 0.070095 0.403812 0.082729
22 POLYGON ((-74.47034 11.0087, -74.47034 11.1008... -830 121 0.074121 0.477932 0.097914
2 POLYGON ((-77.43478 3.98235, -77.43478 4.073, ... -863 44 0.074397 0.552329 0.113155
25 POLYGON ((-74.29067 11.0087, -74.29067 11.1008... -828 121 0.088944 0.641273 0.131377
26 POLYGON ((-74.29067 11.1008, -74.29067 11.1929... -828 122 0.115512 0.756785 0.155042
0 POLYGON ((-77.43478 3.80106, -77.43478 3.8917,... -863 42 0.190937 0.947722 0.194159
1 POLYGON ((-77.43478 3.8917, -77.43478 3.98235,... -863 43 0.237355 1.185077 0.242786
23 POLYGON ((-74.38051 11.0087, -74.38051 11.1008... -829 121 0.331935 1.517012 0.310789
13 POLYGON ((-77.16528 3.98235, -77.16528 4.073, ... -860 44 0.369772 1.886784 0.386544
8 POLYGON ((-77.25511 3.8917, -77.25511 3.98235,... -861 43 0.373268 2.260052 0.463015
9 POLYGON ((-77.25511 3.98235, -77.25511 4.073, ... -861 44 0.491709 2.751761 0.563751
7 POLYGON ((-77.25511 3.80106, -77.25511 3.8917,... -861 42 0.527619 3.279380 0.671844
4 POLYGON ((-77.34495 3.8917, -77.34495 3.98235,... -862 43 0.722770 4.002150 0.819918
3 POLYGON ((-77.34495 3.80106, -77.34495 3.8917,... -862 42 0.879010 4.881160 1.000000

Count AOO cells

  1. Calculate AOO by counting the number of cells with a cumulative proportion greater than 0.01 (i.e. exclude cells that in combination account for up to 1% of the total mapped extent of the ecosystem type).
if has_data:
    aoo = len(gdf[gdf["cumulative_proportion"] > 0.01])
    print(f'AOO is {aoo} cells')
AOO is 23 cells

AOO Calculation (direct call)

if has_data:
    aoo_count = ecosystem.aoo
    print(f'AOO: {aoo_count} grid cells')
AOO: 23 grid cells

Criterion B Summary

Criterion B status (spatial)
Vulnerable (VU) — Fondos Blandos con Vegetacion No Vascular (Fondos Blandos con Vegetacion No Vascular), index 46

Status reflects the spatial thresholds for EOO (B1) and AOO (B2) only. A final listing under B1/B2 additionally requires at least one of: (a) an observed or inferred continuing decline; (b) threatening processes likely to cause continuing decline within 20 years; or (c) few threat-defined locations — none of which are derived from the spatial metrics.

Sub-criterion Metric Value Category
B1 EOO 234303 km² Least Concern (LC)
B2 AOO 23 cells Vulnerable (VU)
Overall B — — Vulnerable (VU)